
#ifndef AFTERMATH_PROBABILITY_DIST_EXPONENTIAL_HPP_INCLUDED
#define AFTERMATH_PROBABILITY_DIST_EXPONENTIAL_HPP_INCLUDED

#include "dist_traits.hpp"

#include <cmath>
#include <string>
#include <utility>

namespace aftermath
{
    namespace probability
    {
        // Forward declaration to define traits.
        struct dist_exponential;

        template <>
        struct is_continuous<dist_exponential>
        {
            typedef dist_exponential distribution_type;

            static const bool value = true;
        };

        struct dist_exponential
        {
            typedef dist_exponential type;
            typedef double result_type;

            static const std::string name;

            static const dist_exponential standard;

        private:
            double m_mu;
            double m_lambda;
            double m_cache_variance;

        public:
            dist_exponential()
                : m_mu(1.0), m_lambda(1.0),
                m_cache_variance(1.0)
            {
            }

            explicit dist_exponential(double mu)
                : m_mu(mu), m_lambda(1.0 / mu),
                m_cache_variance(mu * mu)
            {
            }

            double expected_value() const { return this->m_mu; }
            double variance() const { return this->m_cache_variance; }
            double standard_deviation() const { return this->m_mu; }

            double mean() const { return this->expected_value(); }
            double stddev() const { return this->standard_deviation(); }

            double cdf(double x) const { return x < 0.0 ? 0.0 : (1.0 - std::exp(-this->m_lambda * x)); }
            double pdf(double x) const { return x < 0.0 ? 0.0 : (this->m_lambda * std::exp(-this->m_lambda * x)); }

            bool operator ==(const type& other) const
            {
                return this->m_mu == other.m_mu;
            }

            bool operator !=(const type& other) const
            {
                return !this->operator==(other);
            }
        };
        
        const std::string dist_exponential::name = "exp";
        
        const dist_exponential dist_exponential::standard = dist_exponential(1.0);
    }
}

namespace std
{
    template <>
    struct hash<aftermath::probability::dist_exponential>
    {
        typedef aftermath::probability::dist_exponential argument_type;
        typedef std::size_t result_type;

        result_type operator()(argument_type const& x) const
        {
            return std::hash<double>()(x.expected_value());
        }
    };
}

#endif // AFTERMATH_PROBABILITY_DIST_EXPONENTIAL_HPP_INCLUDED
